package cn.edu.scau.cmi.ema.service;

import java.util.List;
import java.util.Set;

import org.springframework.stereotype.Service;

import org.springframework.transaction.annotation.Transactional;


import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Projections;
import org.hibernate.Session;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Projections;
import org.hibernate.Session;
import cn.edu.scau.cmi.ema.dao.*;
import cn.edu.scau.cmi.ema.domain.*;
import cn.edu.scau.cmi.ema.util.CmiSqlUtil;
import cn.edu.scau.cmi.ema.domain.base.CmiPagination;
import cn.edu.scau.cmi.ema.service.base.CmiEmaService;

import java.io.IOException;
import java.util.Map;
import java.util.HashMap;
import java.util.Set;
import java.util.HashSet;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;

import javax.persistence.Query;
import java.util.Iterator;
@Service("UrlService")
@Transactional

public class UrlServiceImpl extends CmiEmaService implements UrlService {
	public UrlServiceImpl() {
	}

	@Transactional
	public Url saveUrl(Url url) {
		Set<Crawlerfile> existingCrawlerfilesForUrl = new HashSet<Crawlerfile>();
		Set<Crawlerknowledge> existingCrawlerknowledgesForUrl = new HashSet<Crawlerknowledge>();
		Set<Event> existingEventsForUrl = new HashSet<Event>();

		Set<Crawlerfile> selectedCrawlerfilesForUrl = url.getCrawlerfilesForUrl();
		Set<Crawlerknowledge> selectedCrawlerknowledgesForUrl = url.getCrawlerknowledgesForUrl();
		Set<Event> selectedEventsForUrl = url.getEventsForUrl();

		if (url.getId() == null) {
			Site site = siteDAO.findSiteById(url.getSite().getId());
			User author = userDAO.findUserById(url.getAuthor().getId());

			Url existingUrl = urlDAO.findUrlByPhysicalPrimaryKey(url.getValue());

			url.setSite(site);
			url.setAuthor(author);

			if (isEquals(url, existingUrl)) {
				return existingUrl;
			}

			url = urlDAO.store(url);
			urlDAO.flush();

			for (Crawlerfile relatedCrawlerfile : selectedCrawlerfilesForUrl) {
				relatedCrawlerfile.setUrl(url);
				crawlerfileDAO.store(relatedCrawlerfile);
			}
			for (Crawlerknowledge relatedCrawlerknowledge : selectedCrawlerknowledgesForUrl) {
				relatedCrawlerknowledge.setUrl(url);
				crawlerknowledgeDAO.store(relatedCrawlerknowledge);
			}
			for (Event relatedEvent : selectedEventsForUrl) {
				relatedEvent.setUrl(url);
				eventDAO.store(relatedEvent);
			}
		} else {
			Url existingUrl = urlDAO.findUrlByPrimaryKey(url.getId());

			existingCrawlerfilesForUrl = existingUrl.getCrawlerfilesForUrl();
			existingCrawlerknowledgesForUrl = existingUrl.getCrawlerknowledgesForUrl();
			existingEventsForUrl = existingUrl.getEventsForUrl();

			Set<Crawlerfile> prepareDeleteCrawlerfilesForUrl = new HashSet<Crawlerfile>();
			Set<Crawlerfile> prepareAddCrawlerfilesForUrl = new HashSet<Crawlerfile>();

			prepareDeleteCrawlerfilesForUrl.addAll(selectedCrawlerfilesForUrl);
			prepareDeleteCrawlerfilesForUrl.addAll(existingCrawlerfilesForUrl);

			prepareAddCrawlerfilesForUrl.addAll(selectedCrawlerfilesForUrl);
			prepareAddCrawlerfilesForUrl.addAll(existingCrawlerfilesForUrl);

			prepareDeleteCrawlerfilesForUrl.removeAll(selectedCrawlerfilesForUrl);
			prepareAddCrawlerfilesForUrl.removeAll(existingCrawlerfilesForUrl);

			for (Crawlerfile relatedCrawlerfile : prepareAddCrawlerfilesForUrl) {
				relatedCrawlerfile.setUrl(url);
				crawlerfileDAO.store(relatedCrawlerfile);
			}

			for (Crawlerfile relatedCrawlerfile : prepareDeleteCrawlerfilesForUrl) {
				relatedCrawlerfile.setUrl(null);
				crawlerfileDAO.store(relatedCrawlerfile);
			}
			Set<Crawlerknowledge> prepareDeleteCrawlerknowledgesForUrl = new HashSet<Crawlerknowledge>();
			Set<Crawlerknowledge> prepareAddCrawlerknowledgesForUrl = new HashSet<Crawlerknowledge>();

			prepareDeleteCrawlerknowledgesForUrl.addAll(selectedCrawlerknowledgesForUrl);
			prepareDeleteCrawlerknowledgesForUrl.addAll(existingCrawlerknowledgesForUrl);

			prepareAddCrawlerknowledgesForUrl.addAll(selectedCrawlerknowledgesForUrl);
			prepareAddCrawlerknowledgesForUrl.addAll(existingCrawlerknowledgesForUrl);

			prepareDeleteCrawlerknowledgesForUrl.removeAll(selectedCrawlerknowledgesForUrl);
			prepareAddCrawlerknowledgesForUrl.removeAll(existingCrawlerknowledgesForUrl);

			for (Crawlerknowledge relatedCrawlerknowledge : prepareAddCrawlerknowledgesForUrl) {
				relatedCrawlerknowledge.setUrl(url);
				crawlerknowledgeDAO.store(relatedCrawlerknowledge);
			}

			for (Crawlerknowledge relatedCrawlerknowledge : prepareDeleteCrawlerknowledgesForUrl) {
				relatedCrawlerknowledge.setUrl(null);
				crawlerknowledgeDAO.store(relatedCrawlerknowledge);
			}
			Set<Event> prepareDeleteEventsForUrl = new HashSet<Event>();
			Set<Event> prepareAddEventsForUrl = new HashSet<Event>();

			prepareDeleteEventsForUrl.addAll(selectedEventsForUrl);
			prepareDeleteEventsForUrl.addAll(existingEventsForUrl);

			prepareAddEventsForUrl.addAll(selectedEventsForUrl);
			prepareAddEventsForUrl.addAll(existingEventsForUrl);

			prepareDeleteEventsForUrl.removeAll(selectedEventsForUrl);
			prepareAddEventsForUrl.removeAll(existingEventsForUrl);

			for (Event relatedEvent : prepareAddEventsForUrl) {
				relatedEvent.setUrl(url);
				eventDAO.store(relatedEvent);
			}

			for (Event relatedEvent : prepareDeleteEventsForUrl) {
				relatedEvent.setUrl(null);
				eventDAO.store(relatedEvent);
			}
			url = urlDAO.store(url);
			urlDAO.flush();
		}

		crawlerfileDAO.flush();
		crawlerknowledgeDAO.flush();
		eventDAO.flush();
		return url;
	}

	public void deleteUrl(Url url) {
		urlDAO.remove(url);
		urlDAO.flush();
	}

	@Transactional
	public void deleteUrls(Set<Integer> deleteUrlsId) {
		Query query = urlDAO.createNamedQuery("deleteUrlsById", -1, -1);
		query.setParameter(1, deleteUrlsId);
		query.executeUpdate();
	}

	@Transactional
	public Set<Url> loadUrls() {
		return urlDAO.findAllUrls();

	}

	@Transactional
	public Set<Url> loadReferenceUrls(Set<Url> urls) {
		Iterator<Url> urlsIterator = urls.iterator();
		Set<Url> referencedUrls = new HashSet<>();
		while (urlsIterator.hasNext()) {
			Url currentUrl = urlsIterator.next();

			if (!currentUrl.getCrawlerfilesForUrl().isEmpty()) {
				referencedUrls.add(currentUrl);
				continue;
			}

			if (!currentUrl.getCrawlerknowledgesForUrl().isEmpty()) {
				referencedUrls.add(currentUrl);
				continue;
			}

			if (!currentUrl.getEventsForUrl().isEmpty()) {
				referencedUrls.add(currentUrl);
				continue;
			}
		}
		return referencedUrls;
	}

	public Set<Url> loadUrlsByIdSet(Set<Integer> urlIdSet) {

		Iterator<Integer> urlIdIterator = urlIdSet.iterator();
		Set<Url> urlSet = new HashSet<>();
		while (urlIdIterator.hasNext()) {
			Integer id = urlIdIterator.next();
			Url currentUrl = urlDAO.findUrlById(id);
			urlSet.add(currentUrl);
		}
		return urlSet;
	}

	@Transactional
	public List<Url> findAllUrls(Integer startResult, Integer maxRows) {
		return new java.util.ArrayList<Url>(urlDAO.findAllUrls(startResult, maxRows));
	}

	@Transactional
	public Url findUrlByPrimaryKey(Integer id) {
		return urlDAO.findUrlByPrimaryKey(id);
	}

	@Transactional
	public Integer countUrls() {
		return ((Long) urlDAO.createQuerySingleResult("select count(o) from Url o").getSingleResult()).intValue();
	}

	@Transactional
	public Integer countUrls(CmiPagination page) {
		String sql = CmiSqlUtil.createCountHql(page, Url.class);
		return ((Long) urlDAO.createQuerySingleResult(sql).getSingleResult()).intValue();
	}

	/**
	 * search for name with ajax
	 * 
	 */
	@Transactional
	public Integer countUrlsByName(String queryName) {
		Query query = urlDAO.createNamedQuery("countUrlsByName", -1, -1);
		query.setParameter(1, queryName);
		return new Long((long) query.getSingleResult()).intValue();
	}

	@Transactional
	public Integer countUrlsByDetachedCriteria(CmiPagination page) {
		DetachedCriteria detachedCriteria = CmiSqlUtil.createDetachedCriteria(page, Url.class);
		Session session = entityManager.unwrap(Session.class);
		detachedCriteria.setProjection(Projections.rowCount());
		return new Long((long) detachedCriteria.getExecutableCriteria(session).uniqueResult()).intValue();
	}

	public Map<String, ?> loadUrlObjectReferencedSet(Url url) {
		HashMap<String, Object> objectReferencedSetMap = new HashMap<>();
		if (url.getSite() != null) {

			Set<Integer> siteIds = new HashSet<Integer>();
			siteIds.add(url.getSite().getId());
			objectReferencedSetMap.put("siteIds", siteIds);
		}
		if (url.getAuthor() != null) {

			Set<Integer> authorIds = new HashSet<Integer>();
			authorIds.add(url.getAuthor().getId());
			objectReferencedSetMap.put("authorIds", authorIds);
		}

		Set<Integer> crawlerfilesIds = new HashSet<Integer>();
		for (Crawlerfile obj : url.getCrawlerfilesForUrl()) {

			crawlerfilesIds.add(obj.getId());
		}
		objectReferencedSetMap.put("crawlerfilesIds", crawlerfilesIds);
		Set<Integer> crawlerknowledgesIds = new HashSet<Integer>();
		for (Crawlerknowledge obj : url.getCrawlerknowledgesForUrl()) {

			crawlerknowledgesIds.add(obj.getId());
		}
		objectReferencedSetMap.put("crawlerknowledgesIds", crawlerknowledgesIds);
		Set<Integer> eventsIds = new HashSet<Integer>();
		for (Event obj : url.getEventsForUrl()) {

			eventsIds.add(obj.getId());
		}
		objectReferencedSetMap.put("eventsIds", eventsIds);

		return objectReferencedSetMap;
	}

	public Map<String, ?> loadUrlClassReferencedSet(Url url) {
		HashMap<String, Object> classReferencedSetMap = new HashMap<>();
		classReferencedSetMap.put("sites", siteDAO.findAllSites());
		classReferencedSetMap.put("users", userDAO.findAllUsers());
		classReferencedSetMap.put("crawlerfiles", crawlerfileDAO.findAllCrawlerfiles());
		classReferencedSetMap.put("crawlerknowledges", crawlerknowledgeDAO.findAllCrawlerknowledges());
		classReferencedSetMap.put("events", eventDAO.findAllEvents());

		return classReferencedSetMap;
	}

	public boolean isEquals(Url url, Object obj) {
		if (obj == url)
			return true;
		if (!(obj instanceof Url))
			return false;
		Url comparedUrl = (Url) obj;
		if (!CheckPropertyEqual(url.getValue(), comparedUrl.getValue())) {
			return false;
		}
		return true;
	}

	public static <T> boolean CheckPropertyEqual(T originProperty, T comparedProperty) {
		// 基本类型经过参数传递后会自动装箱
		String[] className = originProperty.getClass().getName().split("\\.");
		if (className[0].equals("java") && className[1].equals("math")) {
			return ((Comparable) originProperty).compareTo(comparedProperty) == 0 ? true : false;
		} else {
			return originProperty.equals(comparedProperty);
		}
	}
}

